package user

import (
	"bytes"
	"cross-shard-channel/common"
	"encoding/json"
	"github.com/eywa-protocol/bls-crypto/bls"
	"io/ioutil"
	"net/http"
	"time"
)

// 定义回复请求的函数
func InitUpdateRequest(client *http.Client, cid int, value int) bool {
	// 初始化基本参数
	var requestObject InitUpdate
	var responseObject InitUpdateResponse
	name := "/init-update"

	// get local information about the channel
	ch := UN.GetChannelFromNode(cid)
	ch.Lock()
	advIP := ch.AdvIP
	advChPub := ch.AdvChPub
	ch.Unlock()

	// generate request object
	lockPriForMe, lockPubForMe := GenerateKeyWithPub(advChPub)

	requestObject.Cid = cid
	requestObject.Value = value
	requestObject.LockPub = lockPubForMe

	// generate request body
	bodyStr, err := json.Marshal(requestObject)
	if err != nil {
		println(err.Error())
		return false
	}
	bodyBuffer := bytes.NewBuffer(bodyStr)
	// do request
	url := advIP + name
	request, _ := http.NewRequest("POST", url, bodyBuffer)
	request.Header.Set("Content-type", "application/json")
	response, err := client.Do(request)
	// error process
	if err != nil {
		println(err.Error())
		return false
	} else if response.StatusCode != 200 {
		println("response error")
		return false
	}
	// 模拟网络通信延迟
	time.Sleep(time.Duration(NET_DELAY) * time.Millisecond)

	// 解析回复
	responseBody, _ := ioutil.ReadAll(response.Body)
	err = json.Unmarshal(responseBody, &responseObject)
	if err != nil {
		println("response unmarshal error")
	}

	if !responseObject.Ack == true {
		return false
	}

	ch.Lock()
	ch.AdvLockPubs = append(ch.AdvLockPubs, responseObject.LockPub)
	ch.MyLockPubs = append(ch.MyLockPubs, lockPubForMe)
	ch.MyLockPris = append(ch.MyLockPris, lockPriForMe)
	ch.Unlock()

	//println("Update Init Success")
	return true
}

func UpdateUnlockRequest(client *http.Client, cid int, value int) bool {
	// 初始化基本参数
	var requestObject UpdateUnlock
	var responseObject UpdateUnlockResponse
	name := "/update-unlock"

	// 获取本地信息
	ch := UN.GetChannelFromNode(cid)
	ch.Lock()
	advIP := ch.AdvIP

	advMainPub := ch.Adv
	myChPub := ch.MyChPub
	myChPri := ch.MyChPri

	myChBalance := ch.MyBalance
	advChBalance := ch.AdvBalance

	createUTXO := ch.CreateUTXO
	notifyUTXOA := ch.NotifyUTXOA
	notifyUTXOB := ch.NotifyUTXOB

	lockTime := ch.LifeTime
	// 账户锁定信息
	advLockPub := ch.AdvLockPubs[ch.Version+1]
	myLockPri := ch.MyLockPris[ch.Version+1]
	ch.Unlock()

	// 构建对方的锁定账户
	lockPubB := myChPub.Aggregate(advLockPub)

	// commit 交易构造，这个交易是给对方的
	commitInputs := []common.UTXO{createUTXO, notifyUTXOA, notifyUTXOB}
	commitOutputA := common.UTXO{
		myChPub,
		nil,
		myChBalance,
	}
	commitOutputB := common.UTXO{
		lockPubB,
		nil,
		advChBalance,
	}
	commitOutputs := []common.UTXO{commitOutputB, commitOutputA}
	commitTx := common.GenerateTransactionByUTXO(commitInputs, commitOutputs, 0)
	commitOutputB.Tx_id = commitTx.TxId

	// 构造锁定交易
	unlockInputs := []common.UTXO{commitOutputB}
	unlockOutputUTXO := common.UTXO{
		PublicKey: advMainPub,
		Balance:   commitOutputB.Balance,
	}
	unlockOutputs := []common.UTXO{unlockOutputUTXO}
	unlockTx := common.GenerateTransactionByUTXO(unlockInputs, unlockOutputs, lockTime)
	unlockSigA := unlockTx.Sign(myChPri)

	// 构建request object
	requestObject.Cid = cid
	requestObject.TxCommitB = *commitTx
	requestObject.TxUnlockB = *unlockTx
	requestObject.Sig = unlockSigA
	requestObject.Value = value

	// 固定内容
	// generate request body
	bodyStr, err := json.Marshal(requestObject)
	if err != nil {
		println(err.Error())
		return false
	}
	bodyBuffer := bytes.NewBuffer(bodyStr)
	// do request
	url := advIP + name
	request, _ := http.NewRequest("POST", url, bodyBuffer)
	request.Header.Set("Content-type", "application/json")
	response, err := client.Do(request)
	// error process
	if err != nil {
		println(err.Error())
		return false
	} else if response.StatusCode != 200 {
		println("response error")
		return false
	}
	// 模拟网络通信延迟
	time.Sleep(time.Duration(NET_DELAY) * time.Millisecond)

	// 解析回复
	responseBody, _ := ioutil.ReadAll(response.Body)
	err = json.Unmarshal(responseBody, &responseObject)
	if err != nil {
		println("response unmarshal error")
	}

	// 构造自己unlock交易的签名，方法是用自己的通道账户私钥签名再叠加对方提供的签名
	myLockSig := responseObject.TxUnlockA.Sign(myLockPri)
	responseObject.TxUnlockA.Sigs = []bls.Signature{responseObject.Sig.Aggregate(myLockSig)}

	// 修改状态信息
	ch.Lock()
	ch.TxCommits = append(ch.TxCommits, responseObject.TxCommitA)
	ch.TxUnlocks = append(ch.TxUnlocks, responseObject.TxUnlockA)
	ch.AdvTxCommits = append(ch.AdvTxCommits, *commitTx)
	ch.Unlock()

	if responseObject.TxUnlockA.Verify() {
		//println("Update Unlock Success")
	} else {
		println("ERROR! client update unlock fail")
		return false
	}

	return true
}

func UpdateCommitRequest(client *http.Client, cid int) bool {
	// 初始化基本参数
	var requestObject UpdateCommit
	var responseObject UpdateCommitResponse
	name := "/update-commit"

	// 获取本地信息
	UN.Lock()
	myMainPub := UN.Pub
	myMainPri := UN.Pri
	UN.Unlock()

	ch := UN.GetChannelFromNode(cid)
	ch.Lock()
	advIP := ch.AdvIP
	url := advIP + name // 至此为必须部分

	myChPri := ch.MyChPri
	myNotifyPri := ch.MyNotifyPri

	advCommitTx := ch.AdvTxCommits[len(ch.AdvTxCommits)-1]
	myCommitTx := ch.TxCommits[len(ch.TxCommits)-1]
	chType := ch.Type
	ch.Unlock()

	// 对commit交易进行签名
	requestObject.Cid = cid
	requestObject.MainSigA = advCommitTx.Sign(myMainPri)
	requestObject.ChSigA = advCommitTx.Sign(myChPri)
	requestObject.NotifySigA = advCommitTx.Sign(myNotifyPri)

	// 固定内容
	// generate request body
	bodyStr, err := json.Marshal(requestObject)
	if err != nil {
		println(err.Error())
		return false
	}
	bodyBuffer := bytes.NewBuffer(bodyStr)
	// do request
	request, _ := http.NewRequest("POST", url, bodyBuffer)
	request.Header.Set("Content-type", "application/json")
	response, err := client.Do(request)
	// error process
	if err != nil {
		println(err.Error())
		return false
	} else if response.StatusCode != 200 {
		println("response error")
		return false
	}
	// 模拟网络通信延迟
	time.Sleep(time.Duration(NET_DELAY) * time.Millisecond)

	// 解析回复
	responseBody, _ := ioutil.ReadAll(response.Body)
	err = json.Unmarshal(responseBody, &responseObject)
	if err != nil {
		println("response unmarshal error")
	}

	// 构造commit交易
	myMainSig := myCommitTx.Sign(myMainPri)
	myChSig := myCommitTx.Sign(myChPri)
	myNotifySig := myCommitTx.Sign(myNotifyPri)

	var sig0 bls.Signature
	var sig1 bls.Signature
	var sig2 bls.Signature

	if chType == 0 {
		sig0 = myChSig.Aggregate(responseObject.ChSigB)
		sig1 = myMainSig.Aggregate(responseObject.NotifySigB)
		sig2 = responseObject.MainSigB.Aggregate(myNotifySig)
	} else {
		sig0 = responseObject.ChSigB.Aggregate(myChSig)
		sig1 = responseObject.NotifySigB.Aggregate(myMainSig)
		sig2 = myNotifySig.Aggregate(responseObject.MainSigB)
	}
	myCommitTx.Sigs = []bls.Signature{sig0, sig1, sig2}
	if myCommitTx.Verify() {
		//println("Update Commit Success")
	} else {
		println("Update Commit Fail")
	}

	// 构建退款交易
	refundInputs := []common.UTXO{advCommitTx.Output[1]}
	refundOutputs := []common.UTXO{{
		myMainPub,
		nil,
		advCommitTx.Output[1].Balance,
	}}
	myRefundTx := common.GenerateTransactionByUTXO(refundInputs, refundOutputs, 0)
	sigRefund := myRefundTx.Sign(myChPri)
	myRefundTx.Sigs = []bls.Signature{sigRefund}

	// 更新本地信息
	ch.Lock()
	ch.TxRefunds = []common.Transaction{}

	ch.TxCommits[len(ch.TxCommits)-1].Sigs = []bls.Signature{sig0, sig1, sig2}
	ch.TxRefunds = append(ch.TxRefunds, *myRefundTx)
	ch.Unlock()

	return true
}

func UpdateKeyExchangeRequest(client *http.Client, cid int, value int) bool {
	// 初始化基本参数
	var requestObject UpdateKeyExchange
	var responseObject UpdateKeyExchangeResponse
	name := "/update-keyExchange"

	// get local information about the channel
	UN.Lock()
	myMainPub := UN.Pub
	UN.Unlock()

	ch := UN.GetChannelFromNode(cid)
	ch.Lock()
	advIP := ch.AdvIP

	myChPri := ch.MyChPri
	advCommitTx := ch.AdvTxCommits[ch.Version]
	myLockPri := ch.MyLockPris[ch.Version]
	ch.Unlock()

	// construct request
	requestObject.Cid = cid
	requestObject.Pri = myLockPri

	// generate request body
	bodyStr, err := json.Marshal(requestObject)
	if err != nil {
		println(err.Error())
		return false
	}
	bodyBuffer := bytes.NewBuffer(bodyStr)
	// do request
	url := advIP + name
	request, _ := http.NewRequest("POST", url, bodyBuffer)
	request.Header.Set("Content-type", "application/json")
	response, err := client.Do(request)
	// error process
	if err != nil {
		println(err.Error())
		return false
	} else if response.StatusCode != 200 {
		println("response error")
		return false
	}
	// 模拟网络通信延迟
	time.Sleep(time.Duration(NET_DELAY) * time.Millisecond)

	// 解析回复
	responseBody, _ := ioutil.ReadAll(response.Body)
	err = json.Unmarshal(responseBody, &responseObject)
	if err != nil {
		println("response unmarshal error")
	}

	// 构建惩罚交易
	inputUTXOs := []common.UTXO{common.UTXO{
		Tx_id:     advCommitTx.TxId,
		PublicKey: advCommitTx.Output[0].PublicKey,
		Balance:   advCommitTx.Output[0].Balance,
	}}
	outputUTXOs := []common.UTXO{common.UTXO{
		Tx_id:     nil,
		PublicKey: myMainPub,
		Balance:   advCommitTx.Output[0].Balance,
	}}
	punishTxForAdv := common.GenerateTransactionByUTXO(inputUTXOs, outputUTXOs, 0)
	sig := punishTxForAdv.MultiSign([]bls.PrivateKey{myChPri, responseObject.Pri})
	punishTxForAdv.Sigs = []bls.Signature{sig}

	if !punishTxForAdv.Verify() {
		println("ERROR: Punish construct Fail")
		// 出错的处理展示不管
		return false
	}

	// 更新成功，修改通道状态
	ch.Lock()
	ch.TxPunishs = append(ch.TxPunishs, *punishTxForAdv)
	ch.Version++
	ch.AdvBalance = ch.AdvBalance - value
	ch.MyBalance = ch.MyBalance + value
	ch.Unlock()

	return true
}
